package ws;

import DB.*;
import UI.*;
import java.security.MessageDigest;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import javax.annotation.Resource;
import javax.jws.WebService;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.xml.ws.WebServiceContext;
import javax.xml.ws.handler.MessageContext;

@WebService(serviceName = "RoMaWS")
public class RoMaWS {

    private static final long milli = 21600000; //6timmar i millisekunder
    private EntityManagerFactory fac = Persistence.createEntityManagerFactory("RomaServerPU");
    private EntityManager manager = fac.createEntityManager();
    @Resource
    WebServiceContext wsctx;

    public RoMaWS() {
    }

    private User validate(int id) {
        try {
            MessageContext mctx = wsctx.getMessageContext();
            Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
            List userList = (List) http_headers.get("Username");
            List passList = (List) http_headers.get("Password");
            String name = "";
            String pass = "";
            if (userList != null) {
                name = userList.get(0).toString();
            }
            if (passList != null) {
                pass = passList.get(0).toString();
            }

            User u = manager.find(User.class, id);
            String saltedpassword = pass + u.getSalt();

            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(saltedpassword.getBytes());
            String md5Password = new String(bytes);
            if (u.getPassword().equals(md5Password) && u.getName().equals(name)) {
                return u;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    private User validate() {
        try {
            MessageContext mctx = wsctx.getMessageContext();
            Map http_headers = (Map) mctx.get(MessageContext.HTTP_REQUEST_HEADERS);
            List userList = (List) http_headers.get("Username");
            List passList = (List) http_headers.get("Password");
            String username = "";
            String pass = "";

            if (userList != null) {
                //get username
                username = userList.get(0).toString();
            }

            if (passList != null) {
                //get password
                pass = passList.get(0).toString();
            }

            Query q = manager.createNamedQuery("User.findByName");
            q.setParameter("name", username);
            DB.User u = (DB.User) q.getSingleResult();
            String saltedpassword = pass + u.getSalt();

            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(saltedpassword.getBytes());
            String md5Password = new String(bytes);
            if (u.getPassword().equals(md5Password)) {
                return u;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    @WebMethod(operationName = "validateUser")
    public UI.UserUI validateUser(@WebParam(name = "name") String name, @WebParam(name = "password") String password) {
        UserUI user = null;
        try {
            User u = validate();
            if (u != null) {
                user = new UserUI(u.getId(), u.getName(), u.getEmail());
                Login login = new Login();
                login.setLogontime(new Date());
                Collection<Login> logins = u.getLoginCollection();
                logins.add(login);
                u.setLoginCollection(logins);
                login.setUserid1(u);
                manager.getTransaction().begin();
                manager.persist(u);
                manager.persist(login);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
            }
        } catch (Exception e) {
        }
        return user;
    }

    @WebMethod(operationName = "registerUser")
    public UserUI registerUser(@WebParam(name = "name") String name, @WebParam(name = "email") String email, @WebParam(name = "password") String password) {
        UserUI user = null;
        if (name == null || name.isEmpty() || email == null || email.isEmpty() || password == null || password.isEmpty()) {
            return null;
        }
        try {
            Query q = manager.createNamedQuery("User.findByName");
            q.setParameter("name", name);
            if (q.getResultList().size() > 0) {
                return null;
            }

            String salt = UUID.randomUUID().toString();
            String passwordsalt = password + salt;
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] bytes = digest.digest(passwordsalt.getBytes());
            String hashPass = new String(bytes);

            manager.getTransaction().begin();
            DB.User u = new DB.User(name, email, hashPass, salt);
            manager.persist(u);
            manager.flush();
            manager.clear();
            manager.getTransaction().commit();
            user = new UserUI(u.getId(), u.getName(), u.getEmail());
            return user;
        } catch (Exception e) {
            e.printStackTrace();
            return user;
        }
    }

    @WebMethod(operationName = "getStatusUpdates")
    public List<UI.StatusUI> getStatusUpdates(@WebParam(name = "id") int id) {
        try {
            User u = validate(id);

            if (u != null) {
                ArrayList<StatusUI> msgs = new ArrayList<StatusUI>();
                for (Statusmessage m : u.getStatusmessageCollection()) {
                    msgs.add(new StatusUI(m.getText(), m.getTmeposted(), id, u.getName(), DateModifier.getModifiedDateString(m.getTmeposted())));
                }
                manager.clear();
                return msgs;
            }
        } catch (Exception e) {
            return new ArrayList<StatusUI>();
        }
        return new ArrayList<StatusUI>();
    }

    @WebMethod(operationName = "addStatusMessage")
    public Boolean addStatusMessage(@WebParam(name = "userId") int userId, @WebParam(name = "statusMessage") String statusMessage) {
        try {
            User u = validate(userId);


            if (u != null) {
                Statusmessage msg = new Statusmessage(statusMessage, new Date());
                msg.setSender(u);
                Collection<Statusmessage> m = u.getStatusmessageCollection();
                m.add(msg);
                u.setStatusmessageCollection(m);
                manager.getTransaction().begin();
                manager.merge(u);
                manager.merge(msg);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
                return true;
            }
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
        return false;
    }

    @WebMethod(operationName = "searchAfterUser")
    public List<UI.UserUI> searchAfterUser(@WebParam(name = "search") String search) {
        List<User> queryList = new ArrayList<User>();
        List<UserUI> resultList = new ArrayList<UserUI>();

        try {
            User user = validate();
            if (user != null) {
                Query q = manager.createQuery("Select u from User u where u.name like '%" + search + "%'");
                queryList = q.getResultList();
                for (User u : queryList) {
                    resultList.add(new UserUI(u.getId(), u.getName(), u.getEmail()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resultList;
    }

    @WebMethod(operationName = "addInterest")
    public Boolean addInterest(@WebParam(name = "userId") int userId, @WebParam(name = "newInterest") String newInterest) {
        try {
            User u = validate(userId);
            if (u != null) {
                Query iQ = manager.createNamedQuery("Interest.findByName");
                iQ.setParameter("name", newInterest);
                Interest interest;
                try {
                    interest = (Interest) iQ.getSingleResult();
                } catch (Exception e1) {
                    interest = new Interest(newInterest);
                }
                Collection<Interest> userInterests = u.getInterestCollection();
                userInterests.add(interest);
                Collection<User> interestUsers = interest.getUserCollection();
                interestUsers.add(u);
                manager.getTransaction().begin();
                manager.merge(u);
                manager.merge(interest);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
                return true;
            }
            return false;
        } catch (Exception e2) {
            return false;
        }
    }

    @WebMethod(operationName = "getUserInterests")
    public List<UI.InterestUI> getUserInterests(@WebParam(name = "userId") int userId) {
        List<InterestUI> interests = new ArrayList<InterestUI>();
        try {
            
            User u = validate(userId);
            if (u != null && u.getInterestCollection() != null) {
                for (Interest i : u.getInterestCollection()) {
                    interests.add(new InterestUI(i.getId(), i.getName()));
                }
                return interests;
            }

            return interests;
        } catch (Exception e) {
            e.printStackTrace();
            return interests;
        }
    }

    @WebMethod(operationName = "removeFriendship")
    public Boolean removeFriendship(@WebParam(name = "requester") int req, @WebParam(name = "receiver") int rec) {
        try {
            User requester = validate(req);
            if (requester != null) {
                User receiver = manager.find(User.class, rec);
                Collection<User> reqFriends = requester.getUserCollection();
                reqFriends.remove(receiver);
                requester.setUserCollection(reqFriends);
                Collection<User> recFriends = receiver.getUserCollection();
                recFriends.remove(requester);
                receiver.setUserCollection(recFriends);
                manager.getTransaction().begin();
                manager.merge(requester);
                manager.merge(receiver);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
                return true;
            }
            return false;
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    }

    @WebMethod(operationName = "getFriendStatusUpdates")
    public List<UI.StatusUI> getFriendStatusUpdates(@WebParam(name = "userId") int userId) {
        List<StatusUI> returnList = new ArrayList<StatusUI>();
        try {
            User user = validate(userId);
            if(user != null){
            Collection<Statusmessage> tempList;
            int i = 0;

            for (User tempUser : user.getUserCollection()) {
                i = 0;
                for (Statusmessage m : tempUser.getStatusmessageCollection()) {
                    returnList.add(new StatusUI(m.getText(), m.getTmeposted(), tempUser.getId(), tempUser.getName(), DateModifier.getModifiedDateString(m.getTmeposted())));
                    if (i++ >= 5) {
                        break;
                    }
                }
            }
            Collections.sort(returnList, new StatusUIComparator());
            }
            return returnList;
        } catch (Exception e) {
            e.printStackTrace();
            return returnList;
        }
    }

    @WebMethod(operationName = "getUserFriends")
    public List<UI.UserUI> getUserFriends(@WebParam(name = "userId") int userId) {
        List<UserUI> friends = new ArrayList<UserUI>();
        try {
            User user = validate(userId);
            if (user != null) {
                for (User friend : user.getUserCollection()) {
                    friends.add(new UserUI(friend.getId(), friend.getName(), friend.getEmail()));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return friends;
    }

    @WebMethod(operationName = "addFriendship")
    public Boolean addFriendship(@WebParam(name = "requester") int requester, @WebParam(name = "receiver") int receiver) {
        try {

            if (requester != receiver) {

                User reqUser = validate(requester);
                if (reqUser != null) {
                    User recUser = manager.find(User.class, receiver);
                    Collection<User> uC1 = reqUser.getUserCollection();
                    uC1.add(recUser);
                    reqUser.setUserCollection(uC1);
                    Collection<User> uC2 = recUser.getUserCollection();
                    uC2.add(reqUser);
                    recUser.setUserCollection(uC2);
                    manager.getTransaction().begin();
                    manager.merge(reqUser);
                    manager.merge(recUser);
                    manager.flush();
                    manager.clear();
                    manager.getTransaction().commit();
                    return true;
                }
            }
        } catch (Exception e) {
        }
        return false;
    }

    @WebMethod(operationName = "getReceivedPrivateMessages")
    public List<UI.PMUI> getReceivedPrivateMessages(@WebParam(name = "userId") int userId) {
        User u = validate(userId);
        List<PMUI> messages = new ArrayList<PMUI>();
        if (u != null) {
            for (Privatemessage m : u.getPrivatemessageCollectionReceiver()) {
                PMUI message = new PMUI();
                message.setModifiedDate(DateModifier.getModifiedDateString(m.getTimesent()));
                message.setTitle(m.getTitle());
                message.setSender(m.getSender().getName());
                message.setTimesent(m.getTimesent());
                message.setMessage(m.getMessage());
                message.setReceiver(m.getReceiver().getName());

                messages.add(message);
            }
        }
        return messages;
    }

    @WebMethod(operationName = "getSentPrivateMessages")
    public List<UI.PMUI> getSentPrivateMessages(@WebParam(name = "userId") int userId) {
        User u = validate(userId);
        List<PMUI> messages = new ArrayList<PMUI>();
        if (u != null) {
            for (Privatemessage m : u.getPrivatemessageCollectionSender()) {
                PMUI message = new PMUI();
                message.setModifiedDate(DateModifier.getModifiedDateString(m.getTimesent()));
                message.setTitle(m.getTitle());
                message.setSender(m.getSender().getName());
                message.setTimesent(m.getTimesent());
                message.setMessage(m.getMessage());
                message.setReceiver(m.getReceiver().getName());
                messages.add(message);
            }
        }
        return messages;
    }

    @WebMethod(operationName = "getUserNamesLike")
    public List<java.lang.String> getUserNamesLike(@WebParam(name = "query") String query) {
        List<String> list = new ArrayList<String>();
        User u = validate();
        if (u != null) {
            Query q = manager.createQuery("Select u from User u where u.name like '%" + query + "%'");
            List<User> users = q.getResultList();
            for (User user : users) {
                list.add(user.getName());
            }
        }
        return list;
    }

    @WebMethod(operationName = "sendPrivateMessage")
    public Boolean sendPrivateMessage(@WebParam(name = "receivername") String receivername, @WebParam(name = "title") String title, @WebParam(name = "message") String message, @WebParam(name = "senderId") int senderId) {
        try {

            User sender = validate(senderId);
            if (sender != null) {
                Query query = manager.createNamedQuery("User.findByName");

                query.setParameter("name", receivername);
                User receiver = (User) query.getSingleResult();
                Collection<Privatemessage> privateMessagesReceiver = receiver.getPrivatemessageCollectionReceiver();
                Collection<Privatemessage> privateMessagesSender = sender.getPrivatemessageCollectionSender();
                Privatemessage pm = new Privatemessage();

                pm.setTitle(title);
                pm.setMessage(message);
                pm.setReceiver(receiver);
                pm.setSender(sender);
                pm.setTimesent(new Date());

                privateMessagesReceiver.add(pm);
                privateMessagesSender.add(pm);
                receiver.setPrivatemessageCollection(privateMessagesReceiver);
                sender.setPrivatemessageCollection1(privateMessagesSender);
                manager.getTransaction().begin();
                manager.persist(pm);
                manager.persist(sender);
                manager.persist(receiver);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
                return true;
            }
            return false;
        } catch (Exception e) {
            return false;
        }
    }

    @WebMethod(operationName = "createChatMessage")
    public Boolean createChatMessage(@WebParam(name = "chatId") int chatId, @WebParam(name = "message") String message, @WebParam(name = "userId") int userId) {
        try {
            DB.User sender = validate(userId);
            if (sender != null) {
                Query chatQ = manager.createNamedQuery("Chat.findById");
                chatQ.setParameter("id", chatId);

                DB.Chat chat = (DB.Chat) chatQ.getSingleResult();

                DB.Chatmessage messageDB = new DB.Chatmessage();
                messageDB.setMessage(message);
                messageDB.setSenderId(sender);
                messageDB.setTimesent(new Date());
                messageDB.setChatid(chat);
                Collection<Chatmessage> msg = chat.getChatmessageCollection();
                msg.add(messageDB);
                chat.setChatmessageCollection(msg);
                manager.getTransaction().begin();
                manager.persist(messageDB);
                manager.persist(chat);
                manager.flush();
                manager.clear();
                manager.getTransaction().commit();
                return true;
            }
        } catch (Exception e) {
        }
        return false;
    }

    @WebMethod(operationName = "getChatMessages")
    public List<UI.ChatMessageUI> getChatMessages(@WebParam(name = "chatId") int chatId) {
        Date d = new Date((new Date().getTime() - milli));
        List<ChatMessageUI> chatMessages = new ArrayList<ChatMessageUI>();
        User u = validate();
        try {
            if (u != null) {
                Chat c = manager.find(Chat.class, 1);
                //Query q = manager.createQuery("Select c from ChatMessage c where c.timesent >= :input").setParameter("input", d);
                //List<Chatmessage> msgs = q.getResultList();
                for (Chatmessage m : c.getChatmessageCollection()) {
                    ChatMessageUI cm = new ChatMessageUI();
                    cm.setModifiedDate(DateModifier.getModifiedDateString(m.getTimesent()));
                    cm.setMessage(m.getMessage());
                    cm.setSender(m.getSenderId().getName());
                    cm.setTimeSent(m.getTimesent());
                    chatMessages.add(cm);
                }
            }
            return chatMessages;
        } catch (Exception e) {
            return chatMessages;
        }
    }
}
